package br.com.artsy.library.hibernate;

import java.lang.reflect.Method;

import javax.annotation.PreDestroy;

import net.vidageek.mirror.dsl.Mirror;

import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.SessionFactoryUtils;

import br.com.caelum.vraptor.ioc.ComponentFactory;
import br.com.caelum.vraptor.proxy.MethodInvocation;
import br.com.caelum.vraptor.proxy.Proxifier;
import br.com.caelum.vraptor.proxy.SuperMethod;

public abstract class AbstractSessionCreator implements ComponentFactory<Session> {
    private static final Method CLOSE = new Mirror().on(org.hibernate.classic.Session.class).reflect().method("close").withoutArgs();
    private static final Method FINALIZE = new Mirror().on(Object.class).reflect().method("finalize").withoutArgs();
    
    private final SessionFactory factory;
	private final Session proxy;
	
	//public AbstractSessionCreator(final Proxifier proxifier) {
	public AbstractSessionCreator(final SessionFactory factory, final Proxifier proxifier) { 
		this.proxy = proxify(proxifier);
		this.factory = factory;
	}

	private Session proxify(Proxifier proxifier) {
		return proxifier.proxify(Session.class, new MethodInvocation<Session>() {
		    @Override
		    public Object intercept(Session proxy, Method method, Object[] args, SuperMethod superMethod) {
		        if (method.equals(CLOSE) || (method.equals(FINALIZE) && !hasSession())) {
		            return null; //skip
		        }
		        Session sessionDoSpring = SessionFactoryUtils.doGetSession(factory, true);
		        return new Mirror().on(sessionDoSpring).invoke().method(method).withArgs(args);		        
		        //return new Mirror().on(getSession()).invoke().method(method).withArgs(args);
		    }
		});
	}

	protected abstract Session getSession();

	protected abstract boolean hasSession();
	
	@Override
	public Session getInstance() {
		return proxy;
	}
	
	@PreDestroy
	public void destroy() {
		if (hasSession() && getSession().isOpen())
			getSession().close();
	}
}